﻿#include <iostream>

//定义结构体
struct MyStruct
{
	int id{};
	int age{};
	double wage{};
	int x;       // 无初始化值 (bad)
	int y{};    // 值初始化
	int z{ 2 }; // 显示的默认值
};


//结构体作为参数
void printStruct(const MyStruct& ms) {
	std::cout << ms.id << "\n";
	std::cout << ms.age << "\n";
	std::cout << ms.wage << "\n";
	std::cout << ms.x << "\n";
	std::cout << ms.y << "\n";
	std::cout << ms.z << "\n";
}


//返回结构体
MyStruct getStruct(const MyStruct& ms) {
	return MyStruct{ ms };
}



struct Paw
{
	int claws{};
};

struct Animal
{
	std::string name{};
	Paw paw{};
};


//结构体模板
template <typename T>
struct Pair
{
	T first{};
	T second{};
};

//在函数中使用类模板
constexpr int max(Pair<int> p)
{
	return (p.first < p.second ? p.second : p.first);
}


//数据对类模板
template<typename T, typename U>
void print(std::pair<T, U> p) {
	std::cout << '[' << p.first << ", " << p.second << ']';
}


// 这里是模板别名
// 模板别名必须定义在全局作用域里
template <typename T>
using Coord = Pair<T>; // Coord 是 Pair<T> 的别名


//由于CTAD不适用于函数参数，因此当使用模板别名作为函数参数时，
// 必须显式定义模板别名使用的模板参数。
template <typename T>
void print2(const Coord<T>& c)
{
	std::cout << c.first << ' ' << c.second << '\n';
}

int main()
{
	std::cout << "Hello World!\n";


	//初始化结构体
	MyStruct ms{};
	//赋值给结构体成员,结构体默认情况下不初始化成员,必须初始化后才能使用
	ms.id = 1;
	ms.age = 19;
	ms.wage = 56.78;


	std::cout << ms.id << "\n";
	std::cout << ms.age << "\n";
	std::cout << ms.wage << "\n";

	//结构体的聚合初始化
	MyStruct frank = { 1, 32, 60000.0 }; // 拷贝列表初始化
	MyStruct joe{ 2, 28, 45000.0 };     // 列表初始化 (推荐)
	MyStruct robert(3, 45, 62500.0);  // 直接初始化，使用圆括号 (C++20)
	//初始化器列表中缺少元素
	MyStruct joeee{ 2, 28 }; // joeee.wage 将会被值初始化为 0.0

	//Const结构体
	const MyStruct joe2{ 2, 28 };     // 列表初始化 (推荐)


	//C++20添加了一种新的方法来初始化结构成员,按名字初始化
	MyStruct fff{ .id{1},.age{23},.wage{4.56} };

	//使用相同类型的另一个结构体进行初始化
	MyStruct fff2{ fff };

	//调用结构体作为入参传入的方法
	printStruct(fff);
	printStruct(fff2);

	//获取返回的结构体并打印
	printStruct(getStruct(fff));


	//通常，结构的大小大于或等于 其所有成员的大小之和
	std::cout << sizeof(fff) << "\n";
	std::cout << sizeof(fff2) << "\n";


	//结构体指针
	//使用指针访问成员的值时，请使用指针操作符（->），而不是成员选择操作符（.）。
	MyStruct* mtp = &fff;
	//访问或修改成员
	std::cout << mtp->age << "\n";
	mtp->age = 99;// 推荐: 使用 -> 从对象的指针中进行成员选择
	std::cout << mtp->age << "\n";// 推荐: 使用 -> 从对象的指针中进行成员选择
	std::cout << (*mtp).id << '\n'; // 虽然不优雅但是有效: 解引用指针, 再进行成员选择


	Animal puma{ "Puma", { 5 } };
	Animal* ptr{ &puma };
	// ptr 是指针, 使用 ->
	// paw 不是指针, 使用 .
	std::cout << (ptr->paw).claws << '\n';  //(ptr->paw).claws中，括号是不必要的,有助于可读性


	//使用结构体模板
	Pair<int> p1{ 1,2 };
	std::cout << p1.first << "\n";
	std::cout << p1.second << "\n";

	//在函数中使用类模板
	std::cout << max(p1) << "\n";


	//使用数据对模板入参方法
	std::pair<int, int> p{ 1,2 };
	print(p);
	std::pair<int, Pair<int>> p2{ 1,{11,22} };
	std::cout << "first=" << p2.first << ",second=" <<
		p2.second.first << "," << p2.second.second << "\n";


	//类模板参数推导（Class template argument deduction，CTAD）（C++17）
	std::pair<int, int> p11{ 1, 2 }; // 显示声明 std::pair<int, int> (C++11)
	std::pair p22{ 1, 2 };// CTAD，从初始值列表推导 std::pair<int, int> (C++17)




	using Point = Pair<int>; // 创建类型别名
	Point p111{ 1, 2 };        // 编译器会把 Point 替换为 Pair<int>

	//使用模板别名
	Coord cd{ 1,2 }; // 编译器会把 Coord 替换为 Pair<int>



	//使用模板别名作为函数参数
	print2(cd);


}
